package com.yowasa.xiuxian.engine;

import com.google.common.collect.Lists;
import com.yowasa.xiuxian.engine.battle.buff.BuffInterface;
import com.yowasa.xiuxian.engine.battle.strategy.AttackStrategy;
import com.yowasa.xiuxian.engine.battle.strategy.BattleStrategyEngine;
import com.yowasa.xiuxian.engine.battle.strategy.InherentStrategy;
import com.yowasa.xiuxian.engine.message.battle.AbyssHandler;
import com.yowasa.xiuxian.entity.bo.battle.BattleContent;
import com.yowasa.xiuxian.entity.bo.battle.BuffContent;
import com.yowasa.xiuxian.entity.bo.battle.TempUserBattle;
import com.yowasa.xiuxian.entity.bo.battle.UserBattleContent;
import com.yowasa.xiuxian.entity.bo.user.UserBO;
import com.yowasa.xiuxian.entity.bo.user.UserBattle;
import com.yowasa.xiuxian.entity.enums.InherentTiming;
import com.yowasa.xiuxian.entity.po.XiuXianExploreUser;
import com.yowasa.xiuxian.exception.HandlerException;
import com.yowasa.xiuxian.util.RandomUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 对战引擎
 */
@Component
public class BattleEngine {
    @Autowired
    private BattleStrategyEngine battleStrategyEngine;
    public static final Logger logger = LoggerFactory.getLogger(BattleEngine.class);
    //对战
    public BattleContent battle(UserBO a, UserBO b) {
        BattleContent content = new BattleContent();
        content.setFirst(UserBattleContent.build(a));
        content.setSecond(UserBattleContent.build(b));
        content.setProgress(3500);
        while (!content.getEndFlag()) {
            if (nextTurn(content)) {
                action(content.getFirst(), content.getSecond(), content);
            } else {
                action(content.getSecond(), content.getFirst(), content);
            }
        }
        if (content.getFirst().getBattleInfo().getChp() <= 0 & content.getSecond().getBattleInfo().getChp() > 0) {
            content.setResult(-1);
        }
        if (content.getSecond().getBattleInfo().getChp() <= 0 & content.getFirst().getBattleInfo().getChp() > 0) {
            content.setResult(1);
        }

        return content;
    }

    public BattleContent abyssBattle(UserBO a, UserBO b) {
        BattleContent content = new BattleContent();
        content.setFirst(UserBattleContent.build(a));
        Map<String, Integer> element = a.getElement();
        content.getFirst().getTempBattleInfo().setAtk((int) (content.getFirst().getTempBattleInfo().getAtk() * (1 + element.get("金")/100f)));
        content.getFirst().getTempBattleInfo().setSpell((int) (content.getFirst().getTempBattleInfo().getSpell() * (1 + element.get("火")/100f)));
        content.getFirst().getTempBattleInfo().setDef((int) (content.getFirst().getTempBattleInfo().getDef() * (1 + element.get("土")/100f)));
        content.getFirst().getTempBattleInfo().setRes((int) (content.getFirst().getTempBattleInfo().getRes() * (1 + element.get("水")/100f)));
        content.getFirst().getTempBattleInfo().setHp((int) (content.getFirst().getTempBattleInfo().getHp() * (1 + element.get("木")/100f)));
        content.getFirst().getTempBattleInfo().setChp(content.getFirst().getTempBattleInfo().getHp());
        logger.info("强化后的面板：{}",content.getFirst().getTempBattleInfo().getDesc());
        content.setSecond(UserBattleContent.build(b));
        content.setProgress(3500);
        while (!content.getEndFlag()) {
            if (nextTurn(content)) {
                action(content.getFirst(), content.getSecond(), content);
            } else {
                action(content.getSecond(), content.getFirst(), content);
            }
        }
        if (content.getFirst().getBattleInfo().getChp() <= 0 & content.getSecond().getBattleInfo().getChp() > 0) {
            content.setResult(-1);
        }
        if (content.getSecond().getBattleInfo().getChp() <= 0 & content.getFirst().getBattleInfo().getChp() > 0) {
            content.setResult(1);
        }

        return content;
    }

    public BattleContent exploreBattle(UserBO a, UserBO b, XiuXianExploreUser ae, XiuXianExploreUser be) throws IllegalAccessException {
        BattleContent content = new BattleContent();
        content.setFirst(UserBattleContent.build(a));
        content.setSecond(UserBattleContent.build(b));
        kramaEffect(content,ae,be);
        deleteSkill(content);
        content.setProgress(5000);

        while (!content.getEndFlag()) {
            if (nextTurn(content)) {
                action(content.getFirst(), content.getSecond(), content);
            } else {
                action(content.getSecond(), content.getFirst(), content);
            }
        }
        if (content.getFirst().getBattleInfo().getChp() <= 0 & content.getSecond().getBattleInfo().getChp() > 0) {
            content.setResult(-1);
        }
        if (content.getSecond().getBattleInfo().getChp() <= 0 & content.getFirst().getBattleInfo().getChp() > 0) {
            content.setResult(1);
        }

        return content;
    }

    private void deleteSkill(BattleContent content) {
        List<String> strings = Arrays.asList("山崩岩殂", "星海游鲸", "终天闭幕曲");
        content.getFirst().getBattleStrategyList().removeAll(strings);
        content.getSecond().getBattleStrategyList().removeAll(strings);
    }

    private void kramaEffect(BattleContent content, XiuXianExploreUser ae, XiuXianExploreUser be) throws IllegalAccessException {
        TempUserBattle tempBattleInfoA = content.getFirst().getTempBattleInfo();
        TempUserBattle tempBattleInfoB = content.getSecond().getTempBattleInfo();
        Class<UserBattle> userBattleClass = UserBattle.class;
        Field[] declaredFields = userBattleClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);

            int a = (int) field.get(tempBattleInfoA);
            Integer karma = ae.getKarma();
            field.set(tempBattleInfoA,Math.max(a-karma*100,100));

            int b = (int) field.get(tempBattleInfoB);
            Integer karmb = be.getKarma();
            field.set(tempBattleInfoB,Math.max(b-karmb*100,100));

        }

        content.getFirst().setTempBattleInfo(tempBattleInfoA);
        content.getSecond().setTempBattleInfo(tempBattleInfoB);
    }

    //处理搜魂大法
    public void duelSouHun(BattleContent content) {
        if (content.getResult() == 1) {
            if (RandomUtil.randomInt(1,100)<=10&&content.getFirst().getUserBO().getUserConfig().getStudiedSpecialEffect().contains("搜魂大法")) {
                Set<String> battleStrategyList = content.getSecond().getUserBO().getUserConfig().getBattleStrategyList();
                String choice = RandomUtil.choice(Lists.newArrayList(battleStrategyList));
                content.getFirst().getUserBO().getUserConfig().getCanStudyStrategies().add(choice);
                content.getMsgLog().add(MessageFormat.format("{0}对{1}使用搜魂大法获取到了{2}的修行之法", content.getFirst().getUserBO().getName(), content.getSecond().getUserBO().getName(), choice));
            }
        }
    }

    private void action(UserBattleContent first, UserBattleContent second, BattleContent content) {
        //处理双方的buff
        processBattleBuff(first);
        processBattleBuff(second);

        //处理战斗开始时buff触发效果
        processStartBuff(first, second, content);
        checkEnd(first, second, content);
        if (content.getEndFlag()) {
            return;
        }

        //永续效果处理
        List<InherentStrategy> selfBeginBSList = battleStrategyEngine.matchInherentStrategy(first, content, InherentTiming.SELF_BEGIN);
        selfBeginBSList.forEach(e -> e.action(first, second, content));
        List<InherentStrategy> oppBeginBSList = battleStrategyEngine.matchInherentStrategy(second, content, InherentTiming.OPPONENT_BEGIN);
        oppBeginBSList.forEach(e -> e.action(second, first, content));
        checkEnd(first, second, content);
        if (content.getEndFlag()) {
            return;
        }

        //战斗行动处理
        AttackStrategy battleStrategy = battleStrategyEngine.matchAttackStrategy(first, content);
        battleStrategy.action(first, second, content);
        first.setTurn(first.getTurn() + 1);
        checkEnd(first, second, content);
        if (content.getEndFlag()) {
            return;
        }
        //处理结束时buff触发效果
        processEndBuff(first, second, content);
        checkEnd(first, second, content);
        if (content.getEndFlag()) {
            return;
        }
        //处理Buff计数
        processBuffCount(first);
        processCdCount(first);
        //永续效果处理
        List<InherentStrategy> selfEndBSList = battleStrategyEngine.matchInherentStrategy(first, content, InherentTiming.SELF_END);
        selfEndBSList.forEach(e -> e.action(first, second, content));
        List<InherentStrategy> oppEndBSList = battleStrategyEngine.matchInherentStrategy(second, content, InherentTiming.OPPONENT_END);
        oppEndBSList.forEach(e -> e.action(second, first, content));
        checkEnd(first, second, content);
        if (content.getEndFlag()) {
            return;
        }

        //处理回合数
        content.setTurn(content.getTurn() + 1);
        if (content.getTurn() > 20) {
            content.setEndFlag(true);
            content.getMsgLog().add("双方缠斗许久不分胜负");
        }
    }

    private void checkEnd(UserBattleContent first, UserBattleContent second, BattleContent content) {
        if (first.getBattleInfo().getChp() <= 0 & second.getBattleInfo().getChp() <= 0) {
            content.setEndFlag(true);
            content.getMsgLog().add("双方血量同时归0,不分胜负。");
            return;
        }
        if (first.getBattleInfo().getChp() <= 0) {
            content.setEndFlag(true);
            content.getMsgLog().add(first.getUserBO().getName() + "血量归0," + second.getUserBO().getName() + "赢得了对战。");
            return;
        }
        if (second.getBattleInfo().getChp() <= 0) {
            content.setEndFlag(true);
            content.getMsgLog().add(second.getUserBO().getName() + "血量归0," + first.getUserBO().getName() + "赢得了对战。");
            return;
        }
    }

    private void processCdCount(UserBattleContent first) {
        Map<String, Integer> cds = first.getCd();
        cds.entrySet().forEach(e -> e.setValue(e.getValue() - 1));
    }

    private void processBuffCount(UserBattleContent first) {
        Map<String, BuffContent> buff = first.getBuff();
        buff.entrySet().forEach(e -> e.getValue().setDuration(e.getValue().getDuration() - 1));
        List<String> invalidBuff = buff.entrySet().stream().filter(e -> e.getValue().getDuration() <= 0).map(e -> e.getKey()).collect(Collectors.toList());
        invalidBuff.forEach(e -> {
            first.getBuff().remove(e);
        });
    }

    private void processStartBuff(UserBattleContent first, UserBattleContent second, BattleContent content) {
        first.getBuff().entrySet().forEach(e -> {
            BuffInterface buff = BattleStrategyEngine.buffMap.get(e.getKey());
            buff.beforeBuffAction(first, second, content);
        });
    }

    private void processEndBuff(UserBattleContent first, UserBattleContent second, BattleContent content) {
        first.getBuff().entrySet().forEach(e -> {
            BuffInterface buff = BattleStrategyEngine.buffMap.get(e.getKey());
            buff.afterBuffAction(first, second, content);
        });
    }

    private void processBattleBuff(UserBattleContent first) {
        TempUserBattle otherInfo = first.getBattleInfo();
        TempUserBattle userBattle = first.getTempBattleInfo();
        TempUserBattle buffedBattle = new TempUserBattle();
        first.setBattleInfo(buffedBattle);
        BeanUtils.copyProperties(userBattle, buffedBattle);
        buffedBattle.setChp(otherInfo.getChp());
        buffedBattle.setCmp(otherInfo.getCmp());
        first.getBuff().entrySet().forEach(e -> {
            BuffInterface buff = BattleStrategyEngine.buffMap.get(e.getKey());
            buff.effectBattleInfo(e.getValue(), buffedBattle);
        });
    }


    //判断下回合是谁行动
    private boolean nextTurn(BattleContent content) {
        int index = content.getProgress();
        UserBattleContent a = content.getFirst();
        UserBattleContent b = content.getSecond();

        float aTime = (float) (index - a.getIndex()) / a.getBattleInfo().getSpd();
        float bTime = (float) (index - b.getIndex()) / b.getBattleInfo().getSpd();
        // 下回合状态
        boolean result = aTime < bTime;
        if (result) {
            putIndex(a, b, aTime);
        } else {
            putIndex(b, a, bTime);
        }
        return result;
    }

    //设置位置标志
    private void putIndex(UserBattleContent a, UserBattleContent b, float aTime) {
        b.setIndex(b.getIndex() + (int) (aTime * b.getBattleInfo().getSpd()));
        a.setIndex(0);
    }
}
